﻿#pragma once
#include <stddef.h>
#include <stdint.h>
#include <ctype.h>
#include <stdexcept>
#include <string>
#include <ostream>
#include <sstream>
#include <memory>
#include <vector>
#include <map>
#include <unordered_map>
#include <unordered_set>

namespace mspc {
enum class TokenType {

    // 关键字
    KW_SYNTAX,   // syntax
    KW_PACKAGE,  // package
    KW_IMPORT,   // import
    KW_ENUM,     // enum
    KW_STRUCT,   // struct
    KW_MESSAGE,  // message
    KW_ID,       // id
    KW_REPEATED, // repeated

    // 类型
    TYPE_BOOL,   // bool
    TYPE_CHAR,   // char
    TYPE_INT8,   // int8
    TYPE_INT16,  // int16
    TYPE_INT32,  // int32
    TYPE_INT64,  // int64
    TYPE_UINT8,  // uint8
    TYPE_UINT16, // uint16
    TYPE_UINT32, // uint32
    TYPE_UINT64, // uint64
    TYPE_FLOAT,  // float
    TYPE_DOUBLE, // double
    TYPE_STRING, // string
    TYPE_BYTES,  // bytes

    LBRACE,    // {
    RBRACE,    // }
    LBRACKET,  // [
    RBRACKET,  // ]
    EQUAL,     // =
    LPAREN,    // (
    RPAREN,    // )
    SEMICOLON, // ;
    PIPE,      // |
    COLON,     // :
    COMMA,     // ,
    LESS,      // <
    GREATER,   // >
    DOT,       // .

    IDENTIFIER,     // 标识符
    NUMBER,         // 数值
    STRING_LITERAL, // 字符串字面量

    // 其他
    END_OF_FILE,
    ERROR
};

struct Token {
    TokenType type;
    std::string value;
    size_t line;
    size_t column;

    Token()
        : type(TokenType::ERROR)
        , line(0)
        , column(0) {}

    Token(TokenType t, std::string v, size_t l, size_t c)
        : type(t)
        , value(std::move(v))
        , line(l)
        , column(c) {}

    std::string to_string() const;
};

static inline std::ostream& operator<<(std::ostream& os, const Token& token) {
    os << token.to_string();
    return os;
}

std::unordered_map<std::string, TokenType> init_key_words_table();
std::map<TokenType, std::string> init_key_words_table2();
std::unordered_map<std::string, TokenType> init_type_words_table();
std::map<TokenType, std::string> init_type_words_table2();

inline std::string format_error_message(const std::string& msg, size_t line, size_t column,
                                        const std::string& filename) {
    std::stringstream ss;
    ss << filename << ":" << line << ":" << column << ": " << msg;
    return ss.str();
}

class LexerError : public std::runtime_error {
public:
    LexerError(const std::string& msg, size_t line, size_t column, const std::string& filename)
        : std::runtime_error(format_error_message(msg, line, column, filename)) {}
    LexerError(const std::string& msg)
        : std::runtime_error(msg) {}
};

class ParserError : public std::runtime_error {
public:
    ParserError(const std::string& msg, size_t line, size_t column, const std::string& filename)
        : std::runtime_error(format_error_message(msg, line, column, filename)) {}
    ParserError(const std::string& msg)
        : std::runtime_error(msg) {}
};

template <typename T, typename... ARGS>
inline std::unique_ptr<T> MakeUnique(ARGS&&... args) {
    return std::unique_ptr<T>(new T(std::forward<ARGS>(args)...));
}

class TokenContainer {

private:
    size_t current_;
    std::string filename_;
    std::vector<Token> tokens_;

public:
    TokenContainer(const std::string& filename);
    void add_token(Token token);
    const Token& peek() const;
    const Token& next();
    const Token& previous() const;
    bool is_at_end() const;
    size_t save_position() const;
    void restore_position(size_t position);
    const Token& advance();
    size_t size() const;
    bool empty() const;
    void reset();
    const std::vector<Token>& tokens() const;
    const std::string& filename() const;
    std::string format_error(size_t index, const std::string& message) const;
    std::string format_error(const std::string& message) const;
    size_t current() const;
    void print() const;
};

} // namespace mspc
